#pragma once

#include <queue>
#include <iostream>
#include <pthread.h>

template <class T>
class BlockQueue
{
    static const int defaultCapcity = 20; // 阻塞队列默认极值
public:
    BlockQueue(int maxCapcity = defaultCapcity) : maxCapcity_(maxCapcity)
    {
        pthread_mutex_init(&mutex_, nullptr);
        pthread_cond_init(&cCond, nullptr);
        pthread_cond_init(&pCond, nullptr);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mutex_);
        pthread_cond_destroy(&cCond);
        pthread_cond_destroy(&pCond);
    }

public:
    void push(const T &out)
    {
        pthread_mutex_lock(&mutex_);

        //什么情况需要去条件变量下等待？ --临界资源状态不满足的情况需要，那我们怎么知道临界资源的状态满不满足呢？ if判断
        //做判断也是访问临界资源，需要先加锁
        while (queue_.size() == maxCapcity_)
        {
            pthread_cond_wait(&pCond, &mutex_);
        }
        queue_.push(out);

        //生产了一个数据，可以唤醒一个消费者
        pthread_cond_signal(&cCond);
        std::cout << pthread_self()<<", produce a data: " << out << std::endl;

        pthread_mutex_unlock(&mutex_);
    }

    void pop(T *out)
    {
        pthread_mutex_lock(&mutex_);

        //使用while 防止伪唤醒的情况
        while (queue_.size() == 0)
        {
            pthread_cond_wait(&cCond, &mutex_);
        }
        *out = queue_.front();
        queue_.pop();

        //消费了一个数据，唤醒一个生产者
        pthread_cond_signal(&pCond);
        std::cout << pthread_self()<<", consume a data:" << *out << std::endl;

        pthread_mutex_unlock(&mutex_);
    }

private:
    std::queue<T> queue_;   // 阻塞队列
    int maxCapcity_;        // 极值
    pthread_mutex_t mutex_; // 保證阻塞隊列數據安全的鎖
    pthread_cond_t pCond;   // 生產者的條件變量
    pthread_cond_t cCond;   // 消費者的條件標量
};